package brainfsck

import chisel3._
import chisel3.util._
import scala.io.Source
import java.nio.file.{Files, Paths}
import scala.util.{Try, Using}

class RomPort(addrWidth: Int) extends Bundle {
    val ce = Input(Bool())
    val addr = Input(UInt(addrWidth.W))
    val data = Output(BrainfsckInstr())
}

object SimulatedRom {
    def charToInstr(ch: Char): Option[BrainfsckInstr.Type] = {
        ch match {
            case '>' => Some(BrainfsckInstr.SHIFT_RIGHT)
            case '<' => Some(BrainfsckInstr.SHIFT_LEFT)
            case '+' => Some(BrainfsckInstr.INCREASE)
            case '-' => Some(BrainfsckInstr.DECREASE)
            case '.' => Some(BrainfsckInstr.OUTPUT)
            case ',' => Some(BrainfsckInstr.INPUT)
            case '[' => Some(BrainfsckInstr.BRANCH_RIGHT_ZERO)
            case ']' => Some(BrainfsckInstr.BRANCH_LEFT_NONZERO)
            case _   => None
        }
    }

    def charSeqToInstrSeq(chars: Seq[Char]): Seq[BrainfsckInstr.Type] = {
        chars.map(SimulatedRom.charToInstr(_)).flatten
    }

    def fileToInstrSeq(fileName: String): Seq[BrainfsckInstr.Type] = {
        val chars = Using.resource(Source.fromFile(fileName)) { source => source.toList }
        charSeqToInstrSeq(chars)
    }
}

class SimulatedRom(pcWidth: Int, fileName:String) extends Module {
    val io = IO(new RomPort(pcWidth))

    private val content = VecInit(SimulatedRom.fileToInstrSeq(fileName))

    private val readData = RegEnable(content(io.addr), io.ce)
    io.data := readData
}
